package com.gjh.ouygoing.controller;

import com.gjh.ouygoing.service.RefundInfoService;
import com.gjh.ouygoing.service.WxPayService;
import com.gjh.ouygoing.util.HttpUtils;
import com.gjh.ouygoing.util.WechatPay2ValidatorForRequest;
import com.gjh.ouygoing.vo.R;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.awt.*;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 用来调用微信支付的控制层
 */
@RestController
@CrossOrigin//防止跨域
@Api(tags = "微信支付API")
@RequestMapping("/api/wx-pay")//指定我们的这个支付接口
//@Transactional(rollbackFor = Exception.class)//如果发生了Exception的异常我们就进行事务回滚
@Slf4j
public class WxPayController extends BaseController {
    /**
     * 注入微信支付的接口
     */
    @Autowired
    private WxPayService wxPayService;
    /**
     * 注入我当前的这个签名验证
     */
    @Autowired
    private Verifier verifier;
    /**
     * 操作我的这个退款功能
     */
    @Autowired
    private RefundInfoService refundInfoService;

    /**
     * 用来调用微信支付的接口
     * @param productId 商品Id
     * @return
     */
    @ApiOperation("用来调用微信支付的生产支付二维码信息，实现通用下单")
    @PostMapping("/native/{productId}")
    public R nativePay(@PathVariable Long productId) throws Exception {
        log.info("调用微信支付的方法");
        //返回微信支付二维码，传给前端
        Map<String,Object> map = wxPayService.nativePay(productId);//调用服务层中的支付方法，并且把我们的这个商品编号放入到我们的这个服务层中
        return  R.ok().setData(map);
    }

    /**
     * 用来获得微信发给我的这个回调通知
     * @param request 获得请求参数
     * @param response 返送请求给微信端
     * @return
     */
    @ApiOperation("实现回调接口")
    @PostMapping("/native/notify")//这个请求地址主要是我在响应二维码的时候我把这个响应地址返回给了微信端
    public  String nativeNotify(HttpServletRequest request, HttpServletResponse response){
        Map<String,String> map = new HashMap<>();//应答对象
        Gson gson = new Gson();
        try {//如果说我在支付的过程中出现了失败的应答就会进入到我的这个catch方法当中
            //通过HttpUtil的工具类得到我的这个通知参数,得到当前的这个请求头，因为需要转换成json工具
            String body = HttpUtils.readData(request);
            Map <String,Object> bodyMap = gson.fromJson(body, HashMap.class);//把我的这个请求数据转换成map集合
            //用来查看我是否得到这个结果成功
            String RequestId = (String) bodyMap.get("id");//得到我的这个requestId，就相当于我的这个请求体头的编号
            log.info("支付通知的Id==>{}",RequestId);
            log.info("支付通知的结果==>{}",body);
            //TODO:签名验证
            WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest = new WechatPay2ValidatorForRequest(verifier, RequestId,body);//构造我当前的这个签名验证,得到 我的这个签名对象
            if (!wechatPay2ValidatorForRequest.validate(request)) {//判断我的这个签名验证是否通过,我们进行取反，如果说验签失败就进入这个if判断，抛出我的这个异常信息                log.error("通知验签失败");//使用我们的这个日志来进行输出打印
                response.setStatus(500);
                map.put("code","error");//发送我的这个成功的应答
                map.put("message","通知验签失败");//发送我的这个成功的消息
                return gson.toJson(map);//响应我当前的这个失败的应答
            }
            log.info("验签成功");
            //TODO:微信解密，处理订单
            //用来进行我的这个参数解密
            wxPayService.processOrder(bodyMap);
            //给我的这个请求进行应答
            response.setStatus(200);
            map.put("code","SUCCESS");//发送我的这个成功的应答
            map.put("message","成功");//发送我的这个成功的消息
            return gson.toJson(map);//发送我的这个应答
        } catch (Exception e) {
            response.setStatus(500);
            map.put("code","error");//发送我的这个成功的应答
            map.put("message","失败");//发送我的这个成功的消息
            e.printStackTrace();
            return gson.toJson(map);//响应我当前的这个失败的应答
        }
    }

    /**
     * 用来实现取消当前订单功能
     * @param orderNo 得到我的这个订单编号
     * @return
     */
    @ApiOperation("实现取消当前订单")
    @PostMapping("/cancel/{orderNo}")
    public R cancel (@PathVariable String orderNo) throws Exception {
        log.info("取消订单");
        //用来更改我的这个
        wxPayService.cancelOrder(orderNo);
        return R.ok().setMessage("订单已取消");//返回我当前的这个信息
    }

    /**
     * 测试我的这个服务端请求微信端的查单接口
     * 我的这个订单是否存在
     * @param orderNo
     * @return
     */
    @ApiOperation("测试服务端请求微信端的查单接口")
     @GetMapping("/query/{orderNo}")
    public R queryOrder(@PathVariable String orderNo) throws Exception {
       String result = wxPayService.queryOrder(orderNo);//得到我的这个微信的请求参数然后得到我的这个微信端响应的参数
        return R.ok().setMessage(result);//返回我当前的这个微信端发送的请求信息
    }

    /**
     * 用来查询微信端申请退款的功能
     * @param orderNo
     * @param reason
     * @return
     */
    @ApiOperation("查询微信端申请退款的功能")
    @PostMapping("/refunds/{orderNo}/{reason}")
    public R refunds(@PathVariable String orderNo,@PathVariable String reason) throws IOException {
        log.info("申请退款功能");
        wxPayService.refund(orderNo,reason);//用来创建我的这个申请调用退款的方法
        return R.ok();
    }

    /**
     * 根据我的这个退款的编号去查询相关的商户信息
     * @param refundNo 退款编号
     * @return
     */
    @ApiOperation("根据退款的编号去查询相关的商户信息")
    @GetMapping("/query-refund/{refundNo}")
    public R queryRefund(@PathVariable String refundNo) throws IOException {
        log.info("查询退款订单");
        String result = wxPayService.queryRefund(refundNo);
        log.info("查询出来的订单信息==>{}",result);
        return R.ok().setMessage("查询成功").data("result",result);//返回我的这个退款状态
    }

    /**
     * 处理退款单
     * @param request
     * @param response
     * @return 返回微信端响应的结果信息
     */
    @ApiOperation("处理退款单")
    @PostMapping("/refunds/notify")
     public String refundNotify(HttpServletRequest request,HttpServletResponse response){
         log.info("得到微信端发送的响应");
         Gson gson = new Gson();//得到转换成json的对象
         Map<String,String> map = new HashMap<>();//实例化一个HashMap集合，用来存储信息
        try {
           String body = HttpUtils.readData(request);//得到我的这个请求参数信息
            HashMap bodyMap = gson.fromJson(body, HashMap.class);//得到用户响应的相关信息
            String requestId = (String) bodyMap.get("requestId");//得到我当前的这个请求Id
            WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
                    = new WechatPay2ValidatorForRequest(verifier, requestId, body);//实现签名验证，并且把需要传入的方法放到签名验证器对象中
            if (!wechatPay2ValidatorForRequest.validate(request)) {//调用验证器中的这个验证签名中的方法，判断签名是否正确
                log.error("对不起校验失败");
                response.setStatus(500);//返回我当前的这个响应
                map.put("code","500");//把当前的这个状态码放在这个集合当中进行响应
                map.put("message","失败");//把当前的这个消息进行存入到集合中进行响应
                return gson.toJson(map);//把这个集合转换成map类型进行返回
            }
            wxPayService.reprocessRefund(bodyMap);//用来处理退款单
            //如果响应成功则返回成功的响应码
            map.put("code","200");//把当前的这个状态码放在这个集合当中进行响应
            map.put("message","成功");//把当前的这个消息进行存入到集合中进行响应
            return gson.toJson(map);//把这个集合转换成map类型进行返回
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
     }

    /**
     * 下载我当前的这个交易账单
     * @param billDate 查询日期
     * @param type 交易账单类型
     * @return
     */
     @ApiOperation("下载交易账单")
     @GetMapping("/queryBill/{billDate}/{type}")
     public R queryResult(@PathVariable String billDate,
                           @PathVariable String type) throws Exception {
        log.info("获取交易账单信息");
        String billUrl = wxPayService.queryBill(billDate,type);//得到我的这个交易账单Excel表格下载的路径，然后进行 返回
         return R.ok().setMessage("获取订单Url成功").data("billUrl",billUrl);//响应我的资金信息
     }

    /**
     * 发送下载Excel表格的方法
     * @param billDate 我的这个日期
     * @param type 交易账单类型
     * @return
     */
     @ApiOperation("下载excel表格的方法")
     @GetMapping("/downloadbill/{billDate}/{type}")
     public R downloadBill(
             @PathVariable String billDate,
             @PathVariable String type) throws Exception {
        log.info("账单信息下载");
        String result = wxPayService.downloadBill(billDate,type);//调用service层中的excel表格的下载方法
        return R.ok().data("result",result);//返回我的这个下载表格的地址
     }
}
